package com.mask.token.service;

import com.mask.token.config.MaskTokenProperties;
import com.mask.token.model.TokenInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;

/**
 * JWT Token验证增强服务
 * 提供详细的Token验证和错误处理
 *
 * @author mask
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(name = "mask.token.validation.enabled", havingValue = "true", matchIfMissing = false)
public class MaskTokenValidationService {

    private final MaskTokenProperties properties;
    private final MaskTokenKeyService keyService;

    /**
     * 验证Token并返回详细结果
     */
    public TokenValidationResult validateToken(String token) {
        TokenValidationResult result = new TokenValidationResult();
        result.setToken(token);
        result.setValid(false);
        result.setTimestamp(Instant.now());

        if (token == null || token.trim().isEmpty()) {
            result.setError("Token为空");
            result.setErrorType(TokenErrorType.EMPTY_TOKEN);
            return result;
        }

        try {
            // 解析Token
            Claims claims = Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .parseClaimsJws(token)
                    .getBody();

            // 检查Token是否过期
            Date expiration = claims.getExpiration();
            if (expiration != null && expiration.before(new Date())) {
                result.setError("Token已过期");
                result.setErrorType(TokenErrorType.EXPIRED_TOKEN);
                return result;
            }

            // 检查Token是否即将过期
            long timeToExpire = expiration.getTime() - System.currentTimeMillis();
            if (timeToExpire < properties.getRenewalThreshold().toMillis()) {
                result.setShouldRenew(true);
            }

            // 提取Token信息
            result.setValid(true);
            result.setSubject(claims.getSubject());
            result.setUserId(claims.get("userId", Long.class));
            result.setUsername(claims.get("username", String.class));
            result.setDeviceId(claims.get("deviceId", String.class));
            result.setExpiration(expiration.toInstant());
            result.setIssuedAt(claims.getIssuedAt().toInstant());

            log.debug("Token验证成功: {}", claims.getSubject());
            return result;

        } catch (ExpiredJwtException e) {
            result.setError("Token已过期: " + e.getMessage());
            result.setErrorType(TokenErrorType.EXPIRED_TOKEN);
            log.warn("Token已过期: {}", token);
            return result;

        } catch (SignatureException e) {
            result.setError("Token签名验证失败: " + e.getMessage());
            result.setErrorType(TokenErrorType.INVALID_SIGNATURE);
            log.error("Token签名验证失败: {}, 可能原因: 密钥不匹配", token);
            return result;

        } catch (MalformedJwtException e) {
            result.setError("Token格式错误: " + e.getMessage());
            result.setErrorType(TokenErrorType.MALFORMED_TOKEN);
            log.warn("Token格式错误: {}", token);
            return result;

        } catch (UnsupportedJwtException e) {
            result.setError("不支持的Token格式: " + e.getMessage());
            result.setErrorType(TokenErrorType.UNSUPPORTED_TOKEN);
            log.warn("不支持的Token格式: {}", token);
            return result;

        } catch (IllegalArgumentException e) {
            result.setError("Token参数错误: " + e.getMessage());
            result.setErrorType(TokenErrorType.INVALID_TOKEN);
            log.warn("Token参数错误: {}", token);
            return result;

        } catch (Exception e) {
            result.setError("Token验证失败: " + e.getMessage());
            result.setErrorType(TokenErrorType.UNKNOWN_ERROR);
            log.error("Token验证失败: {}", token, e);
            return result;
        }
    }

    /**
     * 快速验证Token（只检查签名和过期时间）
     */
    public boolean quickValidateToken(String token) {
        try {
            if (token == null || token.trim().isEmpty()) {
                return false;
            }

            Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .parseClaimsJws(token);

            return true;

        } catch (Exception e) {
            log.debug("快速Token验证失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取Token信息（不验证签名）
     */
    public TokenInfo getTokenInfoWithoutValidation(String token) {
        try {
            if (token == null || token.trim().isEmpty()) {
                return null;
            }

            // 解析Token但不验证签名
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                return null;
            }

            // 这里可以添加Base64解码逻辑来获取Token信息
            // 但为了安全起见，我们只返回基本信息
            TokenInfo info = new TokenInfo();
            info.setToken(token);
            info.setExpireTime(Instant.now().plusSeconds(3600)); // 默认1小时后过期
            return info;

        } catch (Exception e) {
            log.warn("获取Token信息失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取签名密钥
     */
    private SecretKey getSigningKey() {
        try {
            return keyService.getSigningKey();
        } catch (Exception e) {
            log.error("获取签名密钥失败", e);
            throw new RuntimeException("获取签名密钥失败", e);
        }
    }

    /**
     * Token验证结果
     */
    public static class TokenValidationResult {
        private String token;
        private boolean valid;
        private boolean shouldRenew;
        private String error;
        private TokenErrorType errorType;
        private String subject;
        private Long userId;
        private String username;
        private String deviceId;
        private Instant expiration;
        private Instant issuedAt;
        private Instant timestamp;

        // Getters and Setters
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }
        public boolean isShouldRenew() { return shouldRenew; }
        public void setShouldRenew(boolean shouldRenew) { this.shouldRenew = shouldRenew; }
        public String getError() { return error; }
        public void setError(String error) { this.error = error; }
        public TokenErrorType getErrorType() { return errorType; }
        public void setErrorType(TokenErrorType errorType) { this.errorType = errorType; }
        public String getSubject() { return subject; }
        public void setSubject(String subject) { this.subject = subject; }
        public Long getUserId() { return userId; }
        public void setUserId(Long userId) { this.userId = userId; }
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getDeviceId() { return deviceId; }
        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
        public Instant getExpiration() { return expiration; }
        public void setExpiration(Instant expiration) { this.expiration = expiration; }
        public Instant getIssuedAt() { return issuedAt; }
        public void setIssuedAt(Instant issuedAt) { this.issuedAt = issuedAt; }
        public Instant getTimestamp() { return timestamp; }
        public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }
    }

    /**
     * Token错误类型
     */
    public enum TokenErrorType {
        EMPTY_TOKEN("空Token"),
        EXPIRED_TOKEN("Token已过期"),
        INVALID_SIGNATURE("签名验证失败"),
        MALFORMED_TOKEN("Token格式错误"),
        UNSUPPORTED_TOKEN("不支持的Token格式"),
        INVALID_TOKEN("Token参数错误"),
        UNKNOWN_ERROR("未知错误");

        private final String description;

        TokenErrorType(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }
    }
}
